#include <stdio.h>
#include "esp_check.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "nvs_storage_hal.h"
#include "ch423.h"

static const char *TAG = "ch423";

static SemaphoreHandle_t xCH423Semaphore; /* aht mutex */
static ch423_io_oc ch423_data_pannel;

/**
 * @brief ch423_i2c_write_reg
 *
 * @param i2c_num
 * @param cmd
 * @param write_buffer
 * @return esp_err_t
 */
static esp_err_t ch423_i2c_write_reg(i2c_port_t i2c_num, uint8_t cmd, uint8_t write_buffer)
{
    return i2c_master_write_to_device(i2c_num, cmd, &write_buffer, 1, portTICK_PERIOD_MS);
}

/**
 * @brief ch423_i2c_read_reg
 *
 * @param i2c_num
 * @param cmd
 * @param read_buffer
 * @return esp_err_t
 */
static esp_err_t ch423_i2c_read_reg(i2c_port_t i2c_num, uint8_t cmd, uint8_t *read_buffer)
{
    return i2c_master_read_from_device(i2c_num, cmd, read_buffer, 1, portTICK_PERIOD_MS);
}

/**
 * @brief ch423_ctrl_OC_L
 *
 * @param ctrl_cmd
 * @return esp_err_t
 */
static esp_err_t ch423_ctrl_OC_L(uint8_t ctrl_cmd)
{
    return ch423_i2c_write_reg(CH423_I2C_MASTER_NUM, CH423_WR_OCL, ctrl_cmd);
}

/**
 * @brief ch423_ctrl_OC_H
 *
 * @param ctrl_cmd
 * @return esp_err_t
 */
static esp_err_t ch423_ctrl_OC_H(uint8_t ctrl_cmd)
{
    return ch423_i2c_write_reg(CH423_I2C_MASTER_NUM, CH423_WR_OCH, ctrl_cmd);
}

/**
 * @brief ch423_read_io
 *
 * @param ch423_io_oc
 * @param read_buffer
 * @return esp_err_t
 */
static esp_err_t ch423_read_io(ch423_io_oc *ch423_io_oc, uint8_t *read_buffer)
{
    esp_err_t ret;

    /* ch423 read io data start */
    ret = ch423_i2c_read_reg(CH423_I2C_MASTER_NUM, CH423_RD_IO, read_buffer);
    /* ch423 read io data end */

#if CH423_DEBUG
    ESP_LOGI(TAG, "read io data: %#X", *read_buffer);
#endif

    /* update to struct pannel start */
    ch423_io_oc->ch423_io = *read_buffer;          /* update io data */
    ch423_io_oc->ch423_oc = ch423_io_oc->ch423_io; /* update oc data */
    /* update to struct pannel end */

    return ret;
}

/**
 * @brief ch423_set_oc_level_private
 *
 * @param ch423_io_oc
 * @param ch423_oc_pin
 * @param level
 * @return esp_err_t
 */
static esp_err_t ch423_set_oc_level_private(ch423_io_oc *ch423_io_oc, ch423_oc_l_enmu ch423_oc_pin, bool level)
{
    esp_err_t ret;

    /* set oc level start */
    switch (ch423_oc_pin)
    {
    case OC_L_0:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x01); /* update oc bit0 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0xFE); /* update oc bit0 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    case OC_L_1:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x02); /* update oc bit1 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0xFD); /* update oc bit1 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    case OC_L_2:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x04); /* update oc bit2 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0xFB); /* update oc bit2 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    case OC_L_3:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x08); /* update oc bit3 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0xF7); /* update oc bit3 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    case OC_L_4:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x10); /* update oc bit4 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0xEF); /* update oc bit4 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    case OC_L_5:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x20); /* update oc bit5 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0xDF); /* update oc bit5 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    case OC_L_6:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x40); /* update oc bit6 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0xBF); /* update oc bit6 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    case OC_L_7:
        if (level)
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc | 0x80); /* update oc bit7 -> high */
        }
        else
        {
            ch423_io_oc->ch423_oc = (ch423_io_oc->ch423_oc & 0x7F); /* update oc bit7 -> low */
        }
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    default: /* all close */
        ch423_io_oc->ch423_oc = 0xFF;
        ret = ch423_ctrl_OC_L(ch423_io_oc->ch423_oc);
        break;
    }
    /* set oc level end */

    return ret;
}

/**
 * @brief ch423_device_init
 *
 * @return esp_err_t
 */
static void ch423_device_init(void)
{
    esp_err_t ret = ESP_OK;

    /* ch423 mutex create start */
    xCH423Semaphore = xSemaphoreCreateMutex(); /* create ch423 mutex */
    if (xCH423Semaphore != NULL)
    {
        ESP_LOGI(TAG, "Creat xCH423Semaphore successfully");
    }
    else
    {
        /* error handler begin */
        ESP_LOGE(TAG, "Creat xCH423Semaphore error. err msg->%s,,%s", __FILE__, __func__);
        vSemaphoreDelete(xCH423Semaphore);
        /* error handler end */
    }
    /* ch423 mutex create end */

    /* init ch423 nvs start */
    nvs_flash_get(CH423_NVS_NAME, CH423_NVS_OC_L_KEY);
    uint8_t ch423_nvs_data = nvs_flash_get(CH423_NVS_NAME, CH423_NVS_OC_L_KEY);
    ESP_LOGI(TAG, "ch423 OC_L:[%#X]", ch423_nvs_data);
    ch423_data_pannel.ch423_io = ch423_nvs_data;
    // nvs_flash_set(CH423_NVS_NAME, CH423_NVS_OC_L_KEY, nvs_flash_get(CH423_NVS_NAME, CH423_NVS_OC_L_KEY));
    /* init ch423 nvs end */

    /* ch423 init mode start */
    ret |= ch423_i2c_write_reg(CH423_I2C_MASTER_NUM, CH423_WR_SET, 0x10); /* [NULL][SLEEP][INTENS][OD_EN][X_INT][DEC_H][DEC_L][IO_OE]B */
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "ch423 init err.[mode set err][%d]", ret);
    }
    /* ch423 init mode end */

    vTaskDelay(pdMS_TO_TICKS(10));

    /* set oc default level start */
    ret |= ch423_ctrl_OC_L(ch423_data_pannel.ch423_io);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "ch423 init err.[OC_L set err][%d]", ret);
    }
    /* set oc default level end */
}

/**
 * @brief ch423_set_oc_level
 *
 * @param ch423_oc_pin (0-7)
 * @param level
 */
void ch423_set_oc_level(ch423_oc_l_enmu ch423_oc_pin, bool level)
{
    if (pdTRUE == xSemaphoreTake(xCH423Semaphore, portMAX_DELAY))
    {
        if (ch423_set_oc_level_private(&ch423_data_pannel, ch423_oc_pin, level) != ESP_OK)
        {
            ESP_LOGE(TAG, "ch423 set oc pin level err.");
        }

        vTaskDelay(pdMS_TO_TICKS(10));

        /* set nvs storage ch423 OC L data start */
        uint8_t io_data;
        ch423_read_io(&ch423_data_pannel, &io_data);
        nvs_flash_set(CH423_NVS_NAME, CH423_NVS_OC_L_KEY, io_data);
        /* set nvs storage ch423 OC L data end */

        xSemaphoreGive(xCH423Semaphore); /* release hlw mutex */
    }
}

/**
 * @brief ch423_get_io_data
 *
 * @return uint8_t
 */
uint8_t ch423_get_io_data(void)
{
#if CH423_DEBUG
    ESP_LOGI(TAG, "ch423 get io data:[%#X]", ch423_data_pannel.ch423_io);
#endif

    return ch423_data_pannel.ch423_io;
}

/**
 * @brief ch423_get_open_num_data
 *
 * @return uint8_t
 */
uint8_t ch423_get_open_num_data(void)
{
    uint8_t buff = 0x01;             /* data bit position */
    uint8_t electrical_open_num = 0; /* electrical open num */
    for (uint8_t i = 0; i < 8; i++)
    {
        if ((ch423_data_pannel.ch423_io & buff) == 0)
        {
            electrical_open_num += 1;
        }
        buff = buff << 1;

#if CH423_DEBUG
        ESP_LOGI(TAG, "electrical_open_num:%d ", electrical_open_num);
#endif
    }

    return electrical_open_num;
}

/**
 * @brief ch423_task
 *
 * @param arg
 */
void ch423_task(void *arg)
{
    /* ch423 init default start */
    ch423_device_init();
    /* ch423 init default end */

    uint8_t io_data;

    while (1)
    {
        if (pdTRUE == xSemaphoreTake(xCH423Semaphore, portMAX_DELAY))
        {
            ch423_read_io(&ch423_data_pannel, &io_data); /* read io data */
            xSemaphoreGive(xCH423Semaphore);             /* release ch423 mutex */
        }

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
